package core

import (
	"api/helper"
	"fmt"
	"os"
	"time"

	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

type Logger struct {
	Level      int
	Directory  string
	Format     string
	MaxSize    int
	MaxAge     int
	MaxBackups int
	Compress   bool
}

func (l *Logger) NewLogger(fields ...zapcore.Field) (logger *zap.Logger) {
	if helper.DirPathExist(l.Directory) != nil {
		os.MkdirAll(l.Directory, os.ModePerm)
	}

	// debug level
	debugLevel := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
		return lev == zap.DebugLevel
	})
	// info level
	infoLevel := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
		return lev == zap.InfoLevel
	})
	// warn level
	warnLevel := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
		return lev == zap.WarnLevel
	})
	// error level
	errorLevel := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
		return lev >= zap.ErrorLevel
	})

	month := time.Now().Format("200601")
	date := time.Now().Format("20060102")

	cores := [...]zapcore.Core{
		l.getEncoderCore(fmt.Sprintf("./%s/%s/debug/%s.log", l.Directory, month, date), debugLevel),
		l.getEncoderCore(fmt.Sprintf("./%s/%s/info/%s.log", l.Directory, month, date), infoLevel),
		l.getEncoderCore(fmt.Sprintf("./%s/%s/warn/%s.log", l.Directory, month, date), warnLevel),
		l.getEncoderCore(fmt.Sprintf("./%s/%s/error/%s.log", l.Directory, month, date), errorLevel),
	}
	logger = zap.New(zapcore.NewTee(cores[:]...), zap.AddCaller())

	logger = logger.WithOptions(zap.AddCaller())
	if len(fields) == 0 {
		return logger
	}
	logger = logger.With(fields...)
	return logger
}

// getEncoderCore
func (l *Logger) getEncoderCore(fileName string, level zapcore.LevelEnabler) (core zapcore.Core) {
	writer := l.getWriteSyncer(fileName) // log cutting
	return zapcore.NewCore(l.getEncoder(), writer, level)
}

func (l *Logger) getEncoderConfig() (config zapcore.EncoderConfig) {
	config = zapcore.EncoderConfig{
		MessageKey:     "message",
		LevelKey:       "level",
		TimeKey:        "time",
		NameKey:        "logger",
		CallerKey:      "caller",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     l.customTimeEncoder,
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.FullCallerEncoder,
	}
	return config
}

// getEncoder log format
func (l *Logger) getEncoder() zapcore.Encoder {
	if l.Format == "json" {
		return zapcore.NewJSONEncoder(l.getEncoderConfig())
	}
	return zapcore.NewConsoleEncoder(l.getEncoderConfig())
}

// CustomTimeEncoder
func (l *Logger) customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02T15:04:05.000Z0700"))
}

func (l *Logger) getWriteSyncer(file string) zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   file,         // directory of log files
		MaxSize:    l.MaxSize,    // Maximum size of log file before cutting (unit: MB)
		MaxBackups: l.MaxBackups, // Maximum number of old files retained
		MaxAge:     l.MaxAge,     // Maximum number of days to keep old files
		Compress:   l.Compress,   // Whether to compress and archive old files
	}
	if l.Level <= int(zap.DebugLevel) {
		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger))
	}
	return zapcore.AddSync(lumberJackLogger)
}
